/*
 * @: 
 * @Version: 1.0
 * @Autor: ziwei
 * @Date: 2021-11-02 14:01:40
 * @LastEditors: ziwei
 * @LastEditTime: 2021-12-05 17:19:55
 */
import * as effectTypes from './effectTypes'
import createChannel from "./createChannel";
import { unstable_batchedUpdates } from 'react-dom';

function runSaga(env, saga,completeCallback) {
  let task = {cancel:()=>next('CANCEL_TASK')}
  let {
    getState,
    dispatch,
    channel
  } = env
  let it = saga(); //执行是生成器 返回迭代器
  function next(value,isError) {
    let result;
    if(hasError){
      result = it.throw(value)
    }else if(value === 'CANCEL_TASK'){//取消任务
      //return  提前结束 相当于breadk
      result = it.return(value);
    }else{
      result = it.next(value);
    }
    let {done,vaule:effect} = result
    let {
      done,
      value: effect
    } = it.next(value);
    if (!done) {
      if(typeof effect[Symbol.iterator]==='function'){
        runSaga(env,effect)
        next();//不会阻止当前saga继续向后走
      }else if(typeof effect.then === 'function'){
        effect.then(next)
      }else{
        switch (effect.type) {
          case effectTypes.TAKE:
            //继续递归 once订阅
            channel.once(effect.actionType, next)
            break;
          case effectTypes.SELECT:
            let selectedState = effect.selector(getState())
            next(selectedState);
          case effectTypes.FORK:
            let forkTask = runSaga(env, effect.saga)
            next(forkTask);
          case effectTypes.CALL:
            effect.fn(...effect.args).then(next);
          case effectTypes.CPS:
            effect.fn(...effect.args,(err,data)=>{
              if(err){
                next(err,true)
              }else{
                next(data)
              }
            });
          case effectTypes.ALL:
            let effects = effect.effects;
            let completeCount = 0;
            if(Array.isArray(effects)){
              let result = [];
              effects.forEach((effect,index)=>{
                runSaga(env,effect,(data)=>{
                  result[index]=data;
                  if(++completeCount===effects.length){
                    next(result)
                  }
                })
              })
            }else{
              let result = {};
              let keys = Object.keys(effects);
              keys.forEach((key)=>{
                runSaga(env,effects[key],(data)=>{
                  result[key]=data;
                  if(++completeCount===key.length){
                    next(result)
                  }
                })
              })
            }
            break;
          case effectTypes.CANCEL:
            effect.task.cancel();
            next();
            break;
          default:
            break;
        }
      }
    }else{
      completeCallback&&completeCallback(effect)
    }
  }
  next();
  return task;
}

export default runSaga